﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;


namespace Layers
{
    /// <summary>
    /// <para>To use this class:</para>
    /// <para>1. Init it through constructor</para>
    /// <para>2. Add layer with layer-name to buffer</para>
    /// <para>3. Set function for layer.</para>
    /// <para>4. In OnPaint event of your custom layer call:</para>
    /// <para>  a. CallFunctions()</para>
    /// <para>  b. Render(e.Graphics)</para>
    /// <para>5. OnResize event call: RefreshContext();</para>
    /// </summary>
    public class InteractiveBuffer
    {
        BufferedGraphicsContext context;
        List<RepresetBuffer> buffers;
        UserControl userControl;
        Rectangle rect;

        /// <summary>
        /// Rectangle where buffer will draw to
        /// </summary>
        /// <param name="r"></param>
        public InteractiveBuffer(UserControl c, Rectangle r)
        {
            this.userControl = c;
            this.rect = r;
            buffers = new List<RepresetBuffer>();

            // Init event for redefine later
            context = BufferedGraphicsManager.Current;
            context.MaximumBuffer = new Size(this.userControl.Width + 1, this.userControl.Width + 1);
        }

        /// <summary>
        /// In user control, user must 
        /// </summary>
        public void RefreshContext()
        {
            context = BufferedGraphicsManager.Current;
            context.MaximumBuffer = new Size(this.userControl.Width + 1, this.userControl.Width + 1);
            foreach (var t in buffers)
            {
                t.Buffer = context.Allocate(this.userControl.CreateGraphics(), this.rect);
            }
        }

        /// <summary>
        /// Add one layer (buffer). The last layer is top most.
        /// </summary>
        /// <param name="layerName">Name of layer for get layer back.</param>
        public void AddLayer(string layerName)
        {
            BufferedGraphics temp = context.Allocate(this.userControl.CreateGraphics(), this.rect);
            RepresetBuffer representBuffer = new RepresetBuffer(layerName, temp);
            buffers.Add(representBuffer);

        }

        /// <summary>
        /// <para>Very important in buffer render </para>
        /// <para>1. It execute function of the (i) buffer.</para> 
        /// <para>2. It help render the i buffer to (i + 1) graphic.</para>
        /// <para>3. Loop through all buffers</para>
        /// <para>4. Finally drawing to the inputed object.</para>
        /// </summary>
        /// <param name="g">The gaphics object that finaly rendered to.</param>
        public void Render(Graphics g)
        {
            int temp = this.buffers.Count - 1;
            for (int i = 0; i < temp; i++)
            {
                this.buffers[i].CallEvent(this.buffers[i].Buffer.Graphics, this.rect);
                this.buffers[i].Buffer.Render(this.buffers[i + 1].Buffer.Graphics);
            }
            this.buffers[temp].CallEvent(this.buffers[temp].Buffer.Graphics, this.rect);
            this.buffers[temp].Buffer.Render(g);
        }

        /// <summary>
        /// Get interactive buffer via it's name
        /// </summary>
        /// <param name="name">The name of buffer when you add to array</param>
        /// <returns>BufferedGraphics</returns>
        public RepresetBuffer this[string name]
        {
            get
            {
                var t = (from x in buffers
                         where x.Name == name
                         select x).First();
                return t;

            }
        }


    }

    public class RepresetBuffer
    {
        string name;
        BufferedGraphics buffer;

        // We can't use dictionay because it has own event.
        public delegate void DrawBufferHandle(Graphics g, Rectangle r);
        public event DrawBufferHandle Func;

        public RepresetBuffer(string name, BufferedGraphics buffer)
        {
            this.name = name;
            this.buffer = buffer;
        }

        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
            }
        }

        public BufferedGraphics Buffer
        {
            get
            {
                return buffer;
            }

            set
            {
                this.buffer = value;
            }
        }

        public void CallEvent(Graphics g, Rectangle r)
        {
            this.Func(g, r);
        }
    }
}
